package model.management;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.Date;
import java.util.Iterator;

import model.entity.Item;
import model.entity.Product;
import model.entity.container.ProductContainer;
import model.entity.container.ProductGroup;
import model.entity.container.StorageUnit;
import model.exceptions.InvalidNameException;
import model.exceptions.NullContainerException;

import org.junit.Before;
import org.junit.Test;

import common.Barcode;
import common.Result;
import common.Size;
import common.util.DateUtils;

public class StorageUnitManagerTest
{
  @Before
  public void setUp()
  {
    ItemManager.clear();
    ProductManager.clear();
    ProductGroupManager.clear();
    StorageUnitManager.clear();
  }

  @Test
  public void validateAddTest() throws InvalidNameException
  {
    Result result = null;

    try
    {
      StorageUnit unit = new StorageUnit("unit");

      // validate valid unit
      result = StorageUnitManager.validateAdd(unit);
      assertNotNull(result);
      assertTrue(result.getStatus());

      StorageUnitManager.add(unit);

      // validate invalid unit
      StorageUnit u2 = new StorageUnit("unit");

      result = StorageUnitManager.validateAdd(u2);
      assertNotNull(result);
      assertFalse(result.getStatus());

      // validate second valid unit
      u2 = new StorageUnit("new unit");
      result = StorageUnitManager.validateAdd(u2);
      assertNotNull(result);
      assertTrue(result.getStatus());
    }
    catch (AssertionError e)
    {
      System.out.printf("Last result: " + result);
      throw e;
    }
  }

  @Test
  public void addTest() throws InvalidNameException
  {
    Result result = null;

    try
    {
      StorageUnit unit = new StorageUnit("unit");

      // add valid unit
      result = StorageUnitManager.add(unit);
      assertNotNull(result);
      assertTrue(result.getStatus());

      Iterator<StorageUnit> itr = StorageUnitManager.getStorageUnits();
      assertNotNull(itr);
      assertTrue(itr.hasNext());
      ProductContainer first = itr.next();
      assertEquals(unit, first);
      assertFalse(itr.hasNext());

      // add duplicate unit
      result = StorageUnitManager.add(unit);
      assertNotNull(result);
      assertFalse(result.getStatus());

      itr = StorageUnitManager.getStorageUnits();
      assertNotNull(itr);
      assertTrue(itr.hasNext());
      first = itr.next();
      assertEquals(unit, first);
      assertFalse(itr.hasNext());

      // add invalid second unit
      StorageUnit u2 = new StorageUnit("unit");

      result = StorageUnitManager.add(u2);
      assertNotNull(result);
      assertFalse(result.getStatus());

      itr = StorageUnitManager.getStorageUnits();
      assertNotNull(itr);
      assertTrue(itr.hasNext());
      first = itr.next();
      assertEquals(unit, first);
      assertFalse(itr.hasNext());

      // add valid second unit
      u2 = new StorageUnit("new unit");

      result = StorageUnitManager.add(u2);
      assertNotNull(result);
      assertTrue(result.getStatus());

      itr = StorageUnitManager.getStorageUnits();
      assertNotNull(itr);
      assertTrue(itr.hasNext());
      first = itr.next();
      assertTrue(unit.equals(first) || u2.equals(first));
      assertTrue(itr.hasNext());
      ProductContainer second = itr.next();
      assertTrue(unit.equals(second) || u2.equals(second));
      assertFalse(itr.hasNext());
    }
    catch (AssertionError e)
    {
      System.out.printf("Last result: " + result);
      throw e;
    }
  }

  @Test
  public void validateRemoveTest() throws InvalidNameException, NullContainerException
  {
    Result r = null;
    try
    {
      StorageUnit u1 = new StorageUnit("unit");

      // validate remove of nonexistant unit
      r = StorageUnitManager.validateRemove(u1);
      assertNotNull(r);
      assertTrue(r.getStatus());

      // validate remove of existant unit without product groups or items
      StorageUnitManager.add(u1);

      r = StorageUnitManager.validateRemove(u1);
      assertNotNull(r);
      assertTrue(r.getStatus());

      // validate remove of unit with items but no groups
      Product p1 = new Product(new Barcode("700000000"), "description", new Size(), 36, 1);
      u1.addProduct(p1);
      Item i1 = new Item(p1, new Barcode(), DateUtils.currentDate(), new Date(), u1);
      u1.addItem(i1);

      r = StorageUnitManager.validateRemove(u1);
      assertNotNull(r);
      assertFalse(r.getStatus());

      // validate remove of unit with group with items and items
      ProductGroup group = new ProductGroup("group", u1, new Size());
      Product p2 = new Product(new Barcode("600000"), "description 2", new Size(), 48, 2);
      group.addProduct(p2);
      Item i2 = new Item(p2, new Barcode(), DateUtils.currentDate(), new Date(), group);
      group.addItem(i2);
      u1.addProductGroup(group);

      r = StorageUnitManager.validateRemove(u1);
      assertNotNull(r);
      assertFalse(r.getStatus());

      // validate remove of unit with group with items but no items
      u1.removeItem(i1);

      r = StorageUnitManager.validateRemove(u1);
      assertNotNull(r);
      assertFalse(r.getStatus());

      // validate remove of unit with group without items and no items -- after they have been
      // added and removed --
      group.removeItem(i2);

      r = StorageUnitManager.validateRemove(u1);
      assertNotNull(r);
      assertTrue(r.getStatus());
    }
    catch (AssertionError e)
    {
      System.out.printf("Last result: " + r);
      throw e;
    }
  }

  @Test
  public void removeTest() throws InvalidNameException, NullContainerException
  {
    Result r = null;
    try
    {
      StorageUnit unit = new StorageUnit("unit");

      // test remove of nonexistant unit
      r = StorageUnitManager.remove(unit);
      assertNotNull(r);
      assertTrue(r.getStatus());

      // test remove of newly added unit
      StorageUnitManager.add(unit);
      r = StorageUnitManager.remove(unit);
      assertNotNull(r);
      assertTrue(r.getStatus());

      // test remove of unit that is not valid to remove (has a product group with an item)
      StorageUnitManager.add(unit);
      ProductGroup group = new ProductGroup("group", unit, new Size());
      Product product = new Product(new Barcode("600000"), "description 2", new Size(), 48, 2);
      group.addProduct(product);
      Item item = new Item(product, new Barcode(), DateUtils.currentDate(), new Date(), group);
      group.addItem(item);
      unit.addProductGroup(group);

      r = StorageUnitManager.remove(unit);
      assertNotNull(r);
      assertFalse(r.getStatus());

      // test remove unit that wasn't valid but now is valid to remove
      group.removeItem(item);
      r = StorageUnitManager.remove(unit);
      assertNotNull(r);
      assertTrue(r.getStatus());

      // test remove of correct unit
      StorageUnitManager.add(unit);
      StorageUnit unit2 = new StorageUnit("unit 2");
      StorageUnitManager.add(unit2);

      r = StorageUnitManager.remove(unit);
      assertNotNull(r);
      assertTrue(r.getStatus());

      // ensure the manager only has one unit left, and that it is the correct unit
      Iterator<StorageUnit> itr = StorageUnitManager.getStorageUnits();
      assertTrue(itr.hasNext());
      assertEquals(unit2, itr.next());
      assertFalse(itr.hasNext());
    }
    catch (AssertionError e)
    {
      System.out.printf("Last result: " + r);
      throw e;
    }
  }

  @Test
  public void validateUpdateTest() throws InvalidNameException
  {
    Result r = null;
    try
    {
      StorageUnit before = new StorageUnit("unit");
      StorageUnit after = new StorageUnit("after");
      StorageUnit duplicate = new StorageUnit("unit");

      StorageUnitManager.add(before);

      // test update of unit to itself
      r = StorageUnitManager.validateUpdate(before, before);
      assertNotNull(r);
      assertTrue(r.getStatus());

      // test update of unit that is not in system
      r = StorageUnitManager.validateUpdate(after, before);
      assertNotNull(r);
      assertFalse(r.getStatus());

      // test update of unit to a duplicate
      StorageUnitManager.add(after);
      r = StorageUnitManager.validateUpdate(after, duplicate);
      assertNotNull(r);
      assertFalse(r.getStatus());

      // test update of unit to one that already exists
      r = StorageUnitManager.validateUpdate(before, after);
      assertNotNull(r);
      assertFalse(r.getStatus());

      // test update of valid before to valid after
      StorageUnitManager.remove(after);
      r = StorageUnitManager.validateUpdate(before, after);
      assertNotNull(r);
      assertTrue(r.getStatus());
    }
    catch (AssertionError e)
    {
      System.out.printf("Last result: " + r);
      throw e;
    }
  }

  @Test
  public void updateTest() throws InvalidNameException
  {
    Result r = null;
    try
    {
      StorageUnit before = new StorageUnit("unit");
      StorageUnit after = new StorageUnit("after");
      StorageUnit duplicate = new StorageUnit("unit");

      StorageUnitManager.add(before);

      // test update of unit to itself
      r = StorageUnitManager.update(before, before);
      assertNotNull(r);
      assertTrue(r.getStatus());

      // test update of unit that is not in system
      r = StorageUnitManager.update(after, before);
      assertNotNull(r);
      assertFalse(r.getStatus());

      // test update of unit to a duplicate
      StorageUnitManager.add(after);
      r = StorageUnitManager.update(after, duplicate);
      assertNotNull(r);
      assertFalse(r.getStatus());

      // test update of unit to one that already exists
      r = StorageUnitManager.update(before, after);
      assertNotNull(r);
      assertFalse(r.getStatus());

      // test update of valid before to valid after
      StorageUnitManager.remove(after);
      r = StorageUnitManager.update(before, after);
      assertNotNull(r);
      assertTrue(r.getStatus());

      // test update of previously updated unit to its original unit
      r = StorageUnitManager.update(after, duplicate);
      assertNotNull(r);
      assertTrue(r.getStatus());
    }
    catch (AssertionError e)
    {
      System.out.printf("Last result: " + r);
      throw e;
    }
  }
}

//

//

//

//

//

//

//

//

//

//

//

//

//

//

//

//

//

//

//

//